﻿/*------------------------------------------------------------------
Copyright (c) 2008 visualSingularity.net. All rights reserved.

This file is part of Hyperbolic control.

Hyperbolic control is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.

Hyperbolic control is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
------------------------------------------------------------------*/

using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Controls;
using System.Windows;
using Hyperbolic.Vectors;
using Hyperbolic.Model;
using System.Windows.Media;
using System.Windows.Input;
using System.Windows.Threading;

namespace Hyperbolic
{
    /// <summary>
    /// Class used to position child elements and capture keyboard and mouse events .
    /// </summary>
    public class ItemsPanel : Canvas
    {
        #region fields
        private NodeModel _animatedNode;
        private EuclidianVector _velocity = new EuclidianVector();
        private EuclidianVector _animationVector = new EuclidianVector();
        Point _startPoint;
        Point _endPoint;
        NodeModel _centeredNode;
        private bool _animating;

        DispatcherTimer _timer = new DispatcherTimer();
        #endregion

        #region properties

        public ITreeModel Model
        {
            get
            {
                return this.DataContext as ITreeModel;
            }
        }

        #endregion

        #region ctor

        public ItemsPanel()
        {
            //_timer.Interval = TimeSpan.FromMilliseconds(30);
            //_timer.Tick += new EventHandler(Timer_Tick);
            //_timer.Start();


            this.MouseMove += new MouseEventHandler(ItemsPanel_MouseMove);
            this.PreviewMouseDown += new MouseButtonEventHandler(ItemsPanel_PreviewMouseDown);
            this.PreviewMouseUp += new MouseButtonEventHandler(ItemsPanel_PreviewMouseUp);
            this.MouseLeave += new MouseEventHandler(ItemsPanel_MouseLeave);

            CompositionTarget.Rendering += UpdatePosition;
        }

        #endregion

        #region methods

        protected override void OnRender(System.Windows.Media.DrawingContext dc)
        {
            base.OnRender(dc);
            this.DrawBranches(dc);

            //this.DrawLine(dc, _p1, _p2, Colors.DarkBlue);
            //this.DrawLine(dc, _startPoint, _endPoint, Colors.DarkRed);
        }

        private void DrawLine(System.Windows.Media.DrawingContext dc, Point p1, Point p2, Color color)
        {
            List<PathFigure> __pathFigures = new List<PathFigure>();
            List<PathSegment> __pathSegments = new List<PathSegment>();

            __pathSegments.Add(new LineSegment(new Point(p2.X, p2.Y), true));
            __pathFigures.Add(new PathFigure(new Point(p1.X, p1.Y), __pathSegments, false));
            SolidColorBrush __brush = new SolidColorBrush(color);
            dc.DrawGeometry(null, new Pen(__brush, 0.3), new PathGeometry(__pathFigures));
        }

        /// <summary>
        ///Draws the branches of the hyperbolic tree.
        /// </summary>
        /// <param name="canvas">The graphic canvas.</param>
        public void DrawBranches(DrawingContext dc)
        {
            if (this.Model != null)
            {
                this.Model.RootNodeModel.DrawBranches(dc);
            }
        }

        private void UpdatePosition(object sender, EventArgs e)
        {
            if (_animating)
            {
                if (_animatedNode != null)
                {
                    _animationVector = new EuclidianVector(_animatedNode.OldEuclidianCoordinates);
                }
                _velocity.X *= 0.8;
                _velocity.Y *= 0.8;
                if (_velocity.GetDistance() > 0.01)
                {
                    this.Translate(_animationVector, _velocity);
                }
                else
                {
                    this.EndAnimation();
                    this.EndTranslation();
                }
            }
        }

        /// <summary> Translate the hyperbolic tree 
        /// so that the given node  is put at the origin of the hyperbolic tree.        
        /// </summary>
        /// <param name="node">The given <see cref="HtDrawNode"/></param>
        public void TranslateToOrigin(NodeModel node)
        {
            if (_animatedNode != node)
            {
                _velocity = new EuclidianVector(node.EuclidianCoordinates);
                _animatedNode = node;
                _animating = true;
            }
        }

        private void EndAnimation()
        {
            _animatedNode = null;
            _animating = false;
        }

        private void EndTranslation()
        {
            if (this.Model != null)
                this.Model.RootNodeModel.EndTranslation();
            //this.InvalidateVisual();
        }

        /// <summary> Translates the hyperbolic tree by the given vector.
        /// </summary>
        /// <param name="zs">The first coordinates.</param>
        /// <param name="ze">The second coordinates.</param>
        public void Translate(EuclidianVector zs, EuclidianVector ze)
        {
            if (this.Model == null)
                return;

            EuclidianVector __zo = new EuclidianVector(this.Model.RootNodeModel.OldEuclidianCoordinates);
            __zo.X = -__zo.X;
            __zo.Y = -__zo.Y;
            EuclidianVector __zs2 = new EuclidianVector(zs);
            __zs2.Translate(__zo);

            EuclidianVector __t = new EuclidianVector();
            double __de = ze.GetSquareDistance();
            double __ds = __zs2.GetSquareDistance();
            double __dd = 1.0 - __de * __ds;
            __t.X = (ze.X * (1.0 - __ds) - __zs2.X * (1.0 - __de)) / __dd;
            __t.Y = (ze.Y * (1.0 - __ds) - __zs2.Y * (1.0 - __de)) / __dd;

            if (__t.IsValid)
            {
                HyperbolicTransformation __to = new HyperbolicTransformation();
                __to.Composition(__zo, __t);

                this.Model.RootNodeModel.Transform(__to);
                this.InvalidateVisual();
            }

        }

        public void Translate(Point startPoint, Point endPoint)
        {
            EuclidianVector __start = new EuclidianVector();
            EuclidianVector __end = new EuclidianVector();
            __start.ProjectionStoE(startPoint.X, startPoint.Y, this.Model.ContainerOrigin, this.Model.ContainerBounds);
            __end.ProjectionStoE(endPoint.X, endPoint.Y, this.Model.ContainerOrigin, this.Model.ContainerBounds);

            if (__start.IsValid && __end.IsValid)
            {
                _endPoint = endPoint;
                this.Translate(__start, __end);
            }
        }

        #endregion

        #region mouse handling

        private void ItemsPanel_MouseLeave(object sender, MouseEventArgs e)
        {
            this.EndTranslation();
        }

        private void ItemsPanel_MouseMove(object sender, MouseEventArgs e)
        {
            ItemsPanel __element = sender as ItemsPanel;
            if (__element != null && e.LeftButton == MouseButtonState.Pressed)
            {
                Point __mousePos = e.GetPosition(__element);
                this.Translate(_startPoint, __mousePos);
            }
        }

        private FrameworkElement GetRootTemplatedParent(ref DependencyObject obj)
        {
            FrameworkElement __element = obj as FrameworkElement;
            if (__element != null && __element.TemplatedParent != null)
            {
                DependencyObject __obj = __element.TemplatedParent;
                __element = this.GetRootTemplatedParent(ref __obj);
            }

            return __element;
        }

        /// <summary>
        /// Finds the parent control in the visual tree.
        /// </summary>
        /// <param name="type">Desired parent's type.</param>
        /// <param name="child">The child control.</param>
        /// <returns>A control of the desired type if any, <code>null</code> otherwise.</returns>
        /// <remarks>DO NOT USE if you have implemented bubbling for your events.This method is not generic in order to be FxCop compliant.</remarks>
        public static DependencyObject FindParent(Type type, DependencyObject child)
        {
          if (child == null)
            return null;

          DependencyObject __parent = VisualTreeHelper.GetParent(child);
          if (__parent != null && __parent.GetType().IsAssignableFrom(type))
            return __parent;
          else
            return FindParent(type, __parent);
        }
        private void ItemsPanel_PreviewMouseDown(object sender, MouseButtonEventArgs e)
        {
            FrameworkElement __source = e.Source as FrameworkElement;
            if (__source == null)
                return;

            NodeModel __nodeModel = null;

            IInputElement __element = sender as IInputElement;
            if (__element != null)
                _startPoint = e.GetPosition(__element);

            HitTestResult __hit = VisualTreeHelper.HitTest(this, _startPoint);

            if (!(__hit.VisualHit is ItemsPanel))
            {
                DependencyObject __obj = __hit.VisualHit;
                FrameworkElement __frameworkElement = FindParent(typeof(ContentPresenter), __obj) as FrameworkElement;
                __nodeModel = __frameworkElement.DataContext as NodeModel;
              
            }
            else
            {
                if (e.ClickCount > 1)
                    this.TranslateToOrigin(this.Model.RootNodeModel);
            }

            if (__nodeModel != null)
            {
                this.TranslateToOrigin(__nodeModel);
                if (!__nodeModel.IsLeaf)
                {
                    if (__nodeModel == _centeredNode)
                        __nodeModel.IsExpanded = !__nodeModel.IsExpanded;
                    else
                        __nodeModel.IsExpanded = true;
                }

                _centeredNode = __nodeModel;
            }


        }

        void ItemsPanel_PreviewMouseUp(object sender, MouseButtonEventArgs e)
        {
            this.EndTranslation();
        }

        #endregion

    }
}
